/*
 * Copyright 2018-2021 guerlab.net and other contributors.
 *
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.guerlab.smart.pay.web.controller.user;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import net.guerlab.commons.exception.ApplicationException;
import net.guerlab.smart.pay.core.domain.PayLogExtends;
import net.guerlab.smart.pay.core.domain.PayOrderDTO;
import net.guerlab.smart.pay.core.enums.PayStatus;
import net.guerlab.smart.pay.core.exception.ExceptionReasonInvalidException;
import net.guerlab.smart.pay.core.exception.ExceptionReasonLengthErrorException;
import net.guerlab.smart.pay.core.exception.PayOrderInvalidException;
import net.guerlab.smart.pay.core.exception.PayStatusErrorException;
import net.guerlab.smart.pay.core.searchparams.PayOrderSearchParams;
import net.guerlab.smart.pay.service.entity.PayLog;
import net.guerlab.smart.pay.service.entity.PayOrder;
import net.guerlab.smart.pay.service.service.PayLogService;
import net.guerlab.smart.pay.service.service.PayOrderService;
import net.guerlab.smart.pay.stream.binders.PayOrderStatusChangeSenderChannel;
import net.guerlab.smart.pay.web.domain.ExceptionDTO;
import net.guerlab.smart.pay.web.excel.PayOrderExcelExport;
import net.guerlab.smart.platform.commons.Constants;
import net.guerlab.smart.platform.excel.ExcelUtils;
import net.guerlab.smart.platform.server.controller.BaseFindController;
import net.guerlab.smart.platform.stream.utils.MessageUtils;
import net.guerlab.smart.user.api.OperationLogApi;
import net.guerlab.smart.user.auth.UserContextHandler;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;

/**
 * 支付订单
 *
 * @author guer
 */
@Tag(name = "支付订单")
@RestController("/user/payOrder")
@RequestMapping("/user/payOrder")
@EnableBinding(PayOrderStatusChangeSenderChannel.class)
public class PayOrderController extends BaseFindController<PayOrderDTO, PayOrder, PayOrderService, PayOrderSearchParams, Long> {

    private PayLogService logService;

    private PayOrderStatusChangeSenderChannel statusChangeSender;

    private OperationLogApi operationLogApi;

    @Override
    protected ApplicationException nullPointException() {
        return new PayOrderInvalidException();
    }

    @Operation(description = "导出Excel", security = @SecurityRequirement(name = Constants.TOKEN))
    @GetMapping("/exportExcel")
    public void exportExcel(HttpServletResponse response, PayOrderSearchParams searchParams) {
        beforeFind(searchParams);
        ExcelUtils.exportExcel(response, getService().selectAll(searchParams), PayOrderExcelExport.class, "PayOrder-" + System.currentTimeMillis());
        operationLogApi.add("导出支付订单列表", UserContextHandler.getUserId(), searchParams);
    }

    @Operation(description = "确认支付", security = @SecurityRequirement(name = Constants.TOKEN))
    @PostMapping("/{id}/confirmPayed")
    @Transactional(rollbackFor = Exception.class)
    public PayOrderDTO confirmPayed(@Parameter(name = "id", required = true) @PathVariable Long id) {
        PayOrder payOrder = findOne0(id);

        if (payOrder.getPayStatus() != PayStatus.WAIT_PAY) {
            throw new PayStatusErrorException();
        }

        PayLogExtends payLogExtends = new PayLogExtends();
        payLogExtends.put("operation_userId", UserContextHandler.getUserIdString());
        payLogExtends.put("operation_name", UserContextHandler.getName());
        payLogExtends.put("operation_departmentId", UserContextHandler.getDepartmentIdString());
        payLogExtends.put("operation_departmentName", UserContextHandler.getDepartmentName());

        PayLog payLog = logService.create(id, "MANUAL_CONFIRM", payLogExtends);
        payLog.setVersion(1L);
        logService.payed(payLog);

        operationLogApi.add("确认支付", UserContextHandler.getUserId(), payOrder);

        return findOne0(id).convert();
    }

    @Operation(description = "标记异常", security = @SecurityRequirement(name = Constants.TOKEN))
    @PostMapping("/{id}/markException")
    @Transactional(rollbackFor = Exception.class)
    public PayOrderDTO markException(@Parameter(name = "id", required = true) @PathVariable Long id, @RequestBody ExceptionDTO dto) {
        String reason = StringUtils.trimToNull(dto.getReason());

        if (reason == null) {
            throw new ExceptionReasonInvalidException();
        } else if (reason.length() > ExceptionDTO.REASON_MAX_LENGTH) {
            throw new ExceptionReasonLengthErrorException();
        }

        findOne0(id);
        getService().markException(id, reason);
        operationLogApi.add("支付订单-标记异常", UserContextHandler.getUserId(), id, reason);

        return findOne0(id).convert();
    }

    @Operation(description = "移除标记异常", security = @SecurityRequirement(name = Constants.TOKEN))
    @PostMapping("/{id}/removeMarkException")
    @Transactional(rollbackFor = Exception.class)
    public PayOrderDTO removeMarkException(@Parameter(name = "id", required = true) @PathVariable Long id) {
        findOne0(id);
        getService().removeExceptionMark(id);
        operationLogApi.add("支付订单-移除异常标记", UserContextHandler.getUserId(), id);

        return findOne0(id).convert();
    }

    @Operation(description = "同步订单信息", security = @SecurityRequirement(name = Constants.TOKEN))
    @PostMapping("/{id}/sync")
    public void sync(@Parameter(name = "id", required = true) @PathVariable Long id) {
        MessageUtils.send(statusChangeSender.output(), findOne0(id));
        operationLogApi.add("支付订单-同步订单信息", UserContextHandler.getUserId(), id);
    }

    @Autowired
    public void setLogService(PayLogService logService) {
        this.logService = logService;
    }

    @Autowired
    public void setStatusChangeSender(PayOrderStatusChangeSenderChannel statusChangeSender) {
        this.statusChangeSender = statusChangeSender;
    }

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    public void setOperationLogApi(OperationLogApi operationLogApi) {
        this.operationLogApi = operationLogApi;
    }
}
